<?php

/**
 * Clix Framework
 * 
 * version: 1.0
 * author: Petr Kratina <petr.kratina@gmail.com>
 * licence: New BSD Licence http://www.opensource.org/licenses/bsd-license.php
 * homepage: http://clix.red-pill.cz/
 * project: http://code.google.com/p/clix-framework/
 **/       

class ClixDb
{
    protected $db; 

    public function __construct($c)
    {
        if (!$this->db = mysql_connect($c['host'], $c['username'], $c['password'])) {
            throw new ClixDbException("Can't connect to MySQL: '{$c['username']}@{$c['host']}'");
        }
        if (!mysql_select_db($c['dbname'], $this->db)) {
            throw new ClixDbException("Can't open database '{$c['dbname']}'.");
        }
        if (!empty($c['charset'])) {
            $this->query("SET NAMES {$c['charset']}");
        }
    }
    
    public function query($s)
    {
        if (!$r=mysql_query($s, $this->db)) {
            throw new ClixDbException("Error executing query: ".mysql_error($this->db));
        }
        return $r;
    }
    
    public function fetchRow($s)
    {
        if ($r = $this->query($s)) {
            if ($w = mysql_fetch_assoc($r)) {
                return $w;
            } else {
                return false;
            }
        }
    }
    
    public function fetchOne($s)
    {
        if ($r = $this->fetchRow($s)) {
            return reset($r);
        }
        return false;
    }
    
    public function fetchAll($s)
    {
        if ($r = $this->query($s)) {
            $t = array();
            while ($w = mysql_fetch_assoc($r)) {
                $t[] = $w;
            }
            return $t;
        }
    }
    
    public function quote($t)
    {
        return "'".mysql_escape_string($t)."'";
    }
}

class Clix
{
    protected $view;
    protected $config;
    protected $uri;
    protected $params;
    protected $raw;
    protected $db;
    protected $actionTemplate;

    public function __construct($c = null, $run = true)
    {
        if (session_id()=='') {
            session_start();
        }
        $this->view = new ClixView();
        $this->view->setScriptsPath('./views');
        
        $this->config = array(
            'clix' => array(
                'base' => ''
            )
        );
        if ($c!=null) {
            $this->config = array_merge($this->config, parse_ini_file($c, true));
        } 
        if (empty($this->config['clix']['base'])) {
            $this->config['clix']['base'] = preg_replace("/\/index.php/i", '', $_SERVER['SCRIPT_NAME']);
        }
        if (isset($_GET['force-mobile'])) {
            $_SESSION['_frmb'] = ($_GET['force-mobile'] == 1); 
        }
        if ((preg_match("/(android)|(webOS)|(nokia)|(Windows CE)|(iphone)|(vodafone)|(PalmSource)|(J2ME)/i", $_SERVER['HTTP_USER_AGENT'])or($_SESSION['_frmb']))and(is_dir('./views/mobile'))) {
            $this->view->_m = true;
        }
        
        $this->view->config = $this->config['clix'];
                
        if (!empty($this->config['db'])) {
            $this->db = new ClixDb($this->config['db']);
        }
        
        if (!empty($this->config['clix']['layout'])) {
            if (($this->config['clix']['layout']=='1')or
                ($this->config['clix']['layout']=='true')) {
                $this->view->setLayout('layout.phtml');    
            } else {
                $this->view->setLayout($this->config['clix']['layout']);
            }
        } elseif ($this->view->exists('layout.phtml')) {
            $this->config['clix']['layout'] = 'layout.phtml';
            $this->view->setLayout('layout.phtml');
        }
        
        $urifilter = array(
            "/\?.*/",
            "/^" . preg_quote($this->config['clix']['base'], '/') . "/i", // remove base uri
            "/^\/+/", // remove first slashes 
            "/\/+$/" // remove last slashes
        );
        
        $this->uri = preg_replace($urifilter, '', $_SERVER['REQUEST_URI']); 
        
        $this->params = explode('/', $this->uri);
        if (empty($this->params[0])) {
            $this->params[0] = 'index';
        }
        $this->raw = $this->params;
        $this->params[0] = strtolower($this->params[0]);
        
        $this->init();
        
        if ($run) {
            $this->run();
        }
    }

    public function _getParam($i, $r = false)
    {
        $p = $this->params;
        if ($r) {
            $p = $this->raw; 
        }
        if (isset($p[$i])) {
            return $p[$i];
        }
        return false;
    }
    
    public function init() { }
    
    public function run()
    {
        $action = $this->camel("{$this->params[0]}Action");
        $view = strtolower(implode('/', $this->params) . '.phtml');
        if (method_exists($this, $action)) {
            $this->actionTemplate = $this->params[0] . '.phtml';
            $this->$action();
            echo $this->view->render($this->actionTemplate);
        } elseif ($this->view->exists($view)) {
            echo $this->view->render($view);
        } elseif (method_exists($this, 'defaultAction')) {
            $action = 'defaultAction';
            $this->actionTemplate = 'default.phtml';
            $this->$action();
            echo $this->view->render($this->actionTemplate);
        } else {
            $this->errorAction();
            echo $this->view->render('error.phtml');
        }
    }
    
    public function camel($t)
    {
        $r = '';
        for ($i = 0; $i < strlen($t); $i++) {
            if ($t[$i]=='-') {
                $i++;
                $r .= strtoupper($t[$i]);
            } else {
                $r .= $t[$i];
            }
        }
        return $r;
    }
    
    public function errorAction()
    {
        header("HTTP/1.0 404 Not Found");
    }
    
    public function _redirect($u)
    {
        if (!preg_match("/^http/i", $u)) {
            header("Location: {$this->config['clix']['base']}{$u}");
        } else {
            header("Location: {$u}");
        }
        exit;
    }
}

class ClixException extends Exception { }

class ClixViewException extends ClixException { }

class ClixDbException extends ClixException { }

class ClixView
{
    protected $_scriptsPath = '';
    protected $_l = '';
    protected $_r = 0;
    public $_m = false;
    
    public function render($f)
    {
        $this->_r++;

        if (($this->_m)&&($this->exists("mobile/{$f}"))) {
            $f = "mobile/{$f}";
        }

        if (!$this->exists($f)) {
            throw new ClixViewException("View file not found '{$this->_scriptsPath}{$f}'.");
        }

        ob_start();
        include("{$this->_scriptsPath}{$f}");
        $c = ob_get_clean();

        if (($this->_r==1)and(!empty($this->_l))) {
            $this->_content = $c;
            $c = $this->render($this->_l);
        }

        return $c;
    }
    
    public function exists($f)
    {
        return file_exists("{$this->_scriptsPath}{$f}");
    }
    
    public function setScriptsPath($p)
    {
        if ($p[strlen($p-1)]!='/') {
            $p .= '/';
        }
        $this->_scriptsPath = $p;
    }
    
    public function setLayout($f)
    {
        if ($this->exists($f)) {
            $this->_l = $f;
        } else {
            throw new ClixViewException("Layout file not found '{$file}'. (script path: {$this->_scriptsPath})");
        }
    }
}